package math;

/**
 * 
 * @author Ryan Hill
 */
public class Vector2D
{
	public static Vector2D add( Vector2D v1, Vector2D v2 )
	{
		Vector2D result = new Vector2D();
		
		result.x = v1.x + v2.x;
		result.y = v1.y + v2.y;
		
		return result;
	}
	
	public static Vector2D subtract( Vector2D v1, Vector2D v2 )
	{
		Vector2D result = new Vector2D();
		
		result.x = v1.x - v2.x;
		result.y = v1.y - v2.y;
		
		return result;
	}
	
	public static Vector2D multiply( Vector2D v1, double d )
	{
		Vector2D result = new Vector2D();
		
		result.x = v1.x * d;
		result.y = v1.y * d;
		
		return result;
	}
	
	public static Vector2D getNormal( Vector2D v1 )
	{
		Vector2D result = new Vector2D();
		
		
		double len = v1.getMagnitude();
		
		if( len > 0 )
		{
			result.x = v1.x / len;
			result.y = v1.y / len;
		}
		
		return result;
	}
	
	public static double dotProduct( Vector2D v1, Vector2D v2 )
	{
		return v1.x*v2.x + v1.y*v2.y ;
	}
	
	private double x, y;
	
	public Vector2D()
	{
		x = y = 0;
	}
	
	public Vector2D( double x, double y )
	{
		this.setX( x );
		this.setY( y );
	}
	
	//use the boolean to differentiate from the other constructor
	public Vector2D( double mag, double ang, boolean b )
	{
		this.x = this.y = 1;
		this.setMagnitude( mag );
		this.setAngle( ang );
	}
	
	public Vector2D( Vector2D v )
	{
		this.setX( v.getX() );
		this.setY( v.getY() );
	}
	
	public Vector2D normalize()
	{
		double len = getMagnitude();
		
		if( len > 0 )
		{
			x /= len;
			y /= len;
		}
		
		return this;
	}
	
	public double distance( Vector2D v )
	{
		return Math.sqrt( Math.pow( this.x - v.x, 2 ) + Math.pow( this.y - v.y, 2 )  );
	}
	
	public double getMagnitude()
	{
		return Math.sqrt( x*x + y*y );
	}
	
	public void setMagnitude( double d )
	{
		this.normalize().multiply( d );
	}
	
	public double getAngle()
	{
		double angle = Math.atan2( -y , x );
		return convertVectorToNorth(angle);
	}
	
	//angle will be set on the basis of up = 0 deg
	public void setAngle( double radians )
	{
		double mag = this.getMagnitude();
		//first, fix it between 0 and 2PI
		if(radians > 2*Math.PI)
			radians = radians % (2*Math.PI);
		if(radians < 0)
			radians = 2*Math.PI - radians % (2*Math.PI);
		radians = convertNorthToVector(radians);
		this.setX( mag * Math.cos( radians ) );
		this.setY( mag * Math.sin( -radians ) );
	}
	
	private double convertNorthToVector(double angle)
	{
		//undo whatever is done in getAngle
		//if greater than 270, 
		if(angle > 3*(Math.PI/2))
			angle = (2*Math.PI - angle) + Math.PI/2;
		else
			angle = -1*(angle - Math.PI/2);
		return angle;
	}
	
	private double convertVectorToNorth(double radians)
	{
		if(radians > Math.PI/2)
			radians = 2*Math.PI - (radians - Math.PI/2);
		else
			radians = Math.PI/2 - radians;
		return radians;
	}
	
	public Vector2D add( Vector2D v )
	{
		x += v.x;
		y += v.y;
		return this;
	}
	
	public Vector2D subtract( Vector2D v )
	{
		x -= v.x;
		y -= v.y;
		return this;
	}
	
	public Vector2D multiply( double d )
	{
		x *= d;
		y *= d;
		return this;
	}
	
	public Vector2D multiply( Vector2D v )
	{
		x *= v.x;
		y *= v.y;
		return this;
	}
	
	//approach zero by an amount
	public void approachZero(double amt)
	{
		if (amt > Math.abs(x))
			x = 0;
		if (amt > Math.abs(y))
			y = 0;
		if (x < 0)
			x += amt;
		else if (x > 0)
			x -= amt;
		if (y < 0)
			y += amt;
		else if (y > 0)
			y -= amt;
	}
	
	public double dotProduct( Vector2D v )
	{
		return x*v.x + y*v.y;
	}

	public void setX( double x ) 
	{
		this.x = x;
	}

	public double getX() 
	{
		return x;
	}

	public void setY( double y ) 
	{
		this.y = y;
	}

	public double getY() 
	{
		return y;
	}

	public String toString()
	{
		return "( " + x + ", " + y + " )";
	}
	
	public boolean equals( Object o )
	{
		if( o instanceof Vector2D )
		{
			Vector2D v = (Vector2D)o;
			
			return Math.abs( v.x - x ) < 0.0001 && Math.abs( v.y - y ) < 0.0001;
				
		}
		
		return false;
	}
}
